/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.fly.config;

import com.fly.service.UserService;
import com.fly.vo.Response;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.ServerRedirectStrategy;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.authentication.ServerAuthenticationEntryPointFailureHandler;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authorization.AuthorizationWebFilter;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.net.URISyntaxException;

import static org.springframework.web.reactive.function.BodyInserters.fromObject;

/**
 * @author Rob Winch
 * @since 5.0
 */
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {

	@Autowired
	private MyServerAuthenticationEntryPoint myServerAuthenticationEntryPoint;
	@Autowired
	private UserService userService;

	@Bean
	public UserDetailsRepositoryReactiveAuthenticationManager userDetailsRepositoryReactiveAuthenticationManager(){
		UserDetailsRepositoryReactiveAuthenticationManager userDetailsRepositoryReactiveAuthenticationManager =
				new UserDetailsRepositoryReactiveAuthenticationManager(userService);
		userDetailsRepositoryReactiveAuthenticationManager.setPasswordEncoder(new BCryptPasswordEncoder());
		return userDetailsRepositoryReactiveAuthenticationManager;
	}

	@Bean
	SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
		return http.csrf().disable()
			// Demonstrate that method security works
			// Best practice to use both for defense in depth
			.authorizeExchange()
			//	.pathMatchers("/login").permitAll()
				.anyExchange().permitAll()
				.and().formLogin().loginPage("/login").authenticationFailureHandler((webFilterExchange, e) -> {
                    String failUre = "/api/loginFail";
                    String message = "";
                    ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();
                    if(e instanceof BadCredentialsException || e instanceof UsernameNotFoundException){
                        message = "用户名或密码错误";
                    }
                    String uri = failUre + "?message=" + message;
                    URI location = null;
                    try {
                        location = new URI(uri);
                    } catch (URISyntaxException e1) {
                        e1.printStackTrace();
                    }
                    return redirectStrategy.sendRedirect(webFilterExchange.getExchange(), location);
                }).authenticationSuccessHandler((webFilterExchange, authentication) -> {
                    String successUrl = "/api/";
                    ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();
                    URI location = null;
                    try {
                        location = new URI(successUrl);
                    } catch (URISyntaxException e1) {
                        e1.printStackTrace();
                    }
                    return redirectStrategy.sendRedirect(webFilterExchange.getExchange(), location);
                }).and().addFilterAt(new AuthorizationWebFilter(new MyReactiveAuthorizationManager()), SecurityWebFiltersOrder.AUTHORIZATION)
				.addFilterAt(upmAuthenticationWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
			//.httpBasic().and()
				.exceptionHandling().authenticationEntryPoint(myServerAuthenticationEntryPoint).
						and().authenticationManager(userDetailsRepositoryReactiveAuthenticationManager())
			.build();
	}

	private AuthenticationWebFilter upmAuthenticationWebFilter() {
		try {
			AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(userDetailsRepositoryReactiveAuthenticationManager());
		//	authenticationFilter.setServerAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(this.entryPoint));
		//	authenticationFilter.setAuthenticationConverter(new CustomAuthenticationConverter());
			//Setting the Context Repo helped
			authenticationFilter.setSecurityContextRepository(new WebSessionServerSecurityContextRepository());
			return authenticationFilter;
		} catch (Exception e) {
			throw new BeanInitializationException("Could not initialize AuthenticationWebFilter.", e);
		}
	}
/*	@Bean
	public MapReactiveUserDetailsService userDetailsService() {
		User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
		UserDetails rob = userBuilder.username("rob").password("rob").roles("USER").build();
		UserDetails admin = userBuilder.username("admin").password("admin").roles("USER", "ADMIN").build();
		return new MapReactiveUserDetailsService(rob, admin);
	}*/
	@Bean
	public UserService userService(){
		return new UserService();
	}
	/*@Bean
	public WebSecurityExpressionRoot webSecurityExpressionRoot(){
		WebSecurityExpressionRoot webSecurityExpressionRoot = new
	}*/

}
